package 函数式编�?.使用Stream;

import java.lang.reflect.Array;
import java.util.Arrays;
/*
输出集合
我们介绍了Stream的几�?常�?�操作：map()、filter()、reduce()。这些操作�?�Stream来�?�可以分为两类，一类是�?换操作，即把一个Stream�?�?为另一个Stream�?
例�?�map()和filter()，另一类是聚合操作，即对Stream的每�?元素进�?��?�算，得到一�?�?定的结果，例如reduce()�?

区分这两种操作是非常重�?�的，因为�?�于Stream来�?�，对其进�?�转换操作并不会触发任何计算！我�?�?以做�?实验�?
*/
import java.util.function.Supplier; 
import java.util.stream.Stream;
public class 输出集合 {
    public static void main(String[] args) {
   	 //static<T> Stream<T> generate(Supplier<? extends T> s)
        Stream<Long> s1 = Stream.generate(new NatualSupplier1());
        Stream<Long> s2 = s1.map(n -> n * n);
        Stream<Long> s3 = s2.map(n -> n - 1);
        System.out.println(s3);// java.util.stream.ReferencePipeline$3@49476842
        System.out.println(Arrays.toString(s3.limit(10).toArray()));//[0, 3, 8, 15, 24, 35, 48, 63, 80, 99]
    }
}

//interface Supplier<T> {
//   T get();
//}
class NatualSupplier1 implements Supplier<Long> {
    long n = 0;
    public Long get() {
        n++;
        return n;
    }
}

/*
因为s1�?一个Long类型的序列，它的元素高达922亿亿�?，但执�?�上述代码，�?不会有任何内存�?�长，也不会有任何�?�算，因为转换操作只�?保存了转换�?�则，无论我�?对一
个Stream�?换�?�少次，都不会有任何实际计算发生�?

而聚合操作则不一样，聚合操作会立刻促使Stream输出它的每一�?元素，并依�?�纳入�?�算，以获得最终结果。所以，对一个Stream进�?�聚合操作，会触发一系列连锁反应�?

Stream<Long> s1 = Stream.generate(new NatualSupplier());
Stream<Long> s2 = s1.map(n -> n * n);
Stream<Long> s3 = s2.map(n -> n - 1);
Stream<Long> s4 = s3.limit(10);
s4.reduce(0, (acc, n) -> acc + n);
我们对s4进�?�reduce()聚合计算，会不断请求s4输出它的每一�?元素。因为s4的上游是s3，它又会向s3请求元素，�?�致s3向s2请求元素，s2向s1请求元素�?
最终，s1从Supplier实例�?请求到真正的元素，并经过一系列�?�?，最终�?�reduce()聚合出结果�?

�?见，聚合操作�?真�?�需要从Stream请求数据的，对一个Stream做聚合�?�算后，结果就不�?一个Stream，而是一�?其他的Java对象�?

【输出为List�?
reduce()�?�?一种聚合操作，如果我们希望把Stream的元素保存到集合，例如List，因为List的元素是�?定的Java对象，因此，把Stream变为List不是一�?�?换操作，
而是一�?聚合操作，它会强制Stream输出每个元素�?

下面的代码演示了如何将一组String先过滤掉空字符串，然后把非空字�?�串保存到List�?�?

import java.util.*;
import java.util.stream.*;
public class Main {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("Apple", "", null, "Pear", "  ", "Orange");
        List<String> list = stream.filter(s -> s != null && !s.isBlank()).collect(Collectors.toList());
        System.out.println(list);
    }
}

把Stream的每�?元素收集到List的方法是调用collect()并传�?Collectors.toList()对象，它实际上是一个Collector实例，通过类似reduce()的操作，
把每�?元素添加到一�?收集器中（实际上是ArrayList）�?

类似的，collect(Collectors.toSet())�?以把Stream的每�?元素收集到Set�?�?

【输出为数】
把Stream的元素输出为数组和输出为List类似，我�?�?需要调用toArray()方法，并传入数组的“构造方法”：

List<String> list = List.of("Apple", "Banana", "Orange");
String[] array = list.stream().toArray(String[]::new);
注意到传入的“构造方法”是String[]::new，它的�?�名实际上是IntFunction<String[]>定义的String[] apply(int)，即传入int参数，获得String[]数组的返回值�?

输出为Map
如果我们要把Stream的元素收集到Map�?，就稍微麻烦一点。因为�?�于每个元素，添加到Map时需要key和value，因此，我们要指定两�?映射函数，分�?把元素映射为key和value�?

import java.util.*;
import java.util.stream.*;
public class Main {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("APPL:Apple", "MSFT:Microsoft");
        Map<String, String> map = stream
                .collect(Collectors.toMap(
                        // 把元素s映射为key:
                        s -> s.substring(0, s.indexOf(':')),
                        // 把元素s映射为value:
                        s -> s.substring(s.indexOf(':') + 1)));
        System.out.println(map);
    }
}

 Run
分组输出
Stream还有一�?强大的分组功能，�?以按组输出。我�?看下面的例子�?

import java.util.*;
import java.util.stream.*;
public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado", "Cherry", "Apricots");
        Map<String, List<String>> groups = list.stream()
                .collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()));
        System.out.println(groups);
    }
}

 Run
分组输出使用Collectors.groupingBy()，它需要提供两�?函数：一�?�?分组的key，这里使用s -> s.substring(0, 1)，表示只要�?�字母相同的String分到一组，�?二个�?分组的value，这里直接使用Collectors.toList()，表示输出为List，上述代码运行结果�?�下�?

{
    A=[Apple, Avocado, Apricots],
    B=[Banana, Blackberry],
    C=[Coconut, Cherry]
}
�?见，结果一共有3组，�?"A"�?"B"�?"C"分组，每一组都�?一个List�?

假�?�有这样一个Student类，包含学生姓名、班级和成绩�?

class Student {
    int gradeId; // 年级
    int classId; // �?�?
    String name; // 名字
    int score; // 分数
}
如果我们有一个Stream<Student>，利用分组输出，�?以非常简单地按年级或�?级把Student归类�?

小结
Stream�?以输出为集合�?

Stream通过collect()方法�?以方便地输出为List、Set、Map，还�?以分组输出�?
*/